import ast
import inspect
import os
import re
from string import Template
from typing import List, Callable, Tuple

import click
from dotenv import load_dotenv
from openai import OpenAI
import platform

from prompt_template import react_system_prompt_template

class React:
    def __init__(self, tools: List[Callable], file):
        self.model = OpenAI(base_url="",
                            api_key="")
        self.file = file
        self.tools = { func.__name__: func for func in tools }
        self.prompt = self.render_prompt(react_system_prompt_template)

    def render_prompt(self, tempelate):
        """渲染提示词模板"""
        tool_list = self.get_tools()
        file_list = os.path.abspath(os.path.join(os.getcwd(), self.file))
        return Template(tempelate).substitute(
            operating_system=self.get_operating_system_name(),
            tool_list=tool_list,
            file_list=file_list
        )

    def get_operating_system_name(self):
        """"获取系统环境"""
        os_map = {
            "Darwin": "macOS",
            "Windows": "Windows",
            "Linux": "Linux"
        }

        return os_map.get(platform.system(), "Unknown")

    def get_tools(self):
        """获取工具列表"""
        tool_descriptions = []
        for fun in self.tools.values():
            name = fun.__name__
            signature = str(inspect.signature(fun))
            description = inspect.getdoc(fun)
            tool_descriptions.append(f"- {name}{signature}:{description}")
        return "\n".join(tool_descriptions)

    def run(self, user_input):
        messages = [
            {
                "role": "system",
                "content":self.prompt
            },
            {
                "role":"user",
                "content":f"<question>{user_input}</question>"
            }
        ]

        while True:
            # 请求模型
            response = self.model.chat.completions.create(
                model="qwen-plus",
                messages=messages
            )
            content = response.choices[0].message.content
            messages.append({
                "role":"assistant",
                "content":content
            })

            # 检测 thought
            thought_match = re.search(r"<thought>(.*?)</thought>", content, re.DOTALL)
            if thought_match:
                thought_content = thought_match.group(1)
                print(f"thought_content: {thought_content}")

            # 检测 final_answer
            if "<final_answer>" in content:
                final_answer = re.search(r"<final_answer>(.*?)</final_answer>", content, re.DOTALL)
                return final_answer.group(1)

            # 检测 action
            action_match = re.search(r"<action>(.*?)</action>", content, re.DOTALL)
            if not action_match:
                raise RuntimeError("模型未输出 <action>")
            action_content = action_match.group(1).strip()
            print(f"检测到行动：{action_content}")
            try:
                # 解析并执行动作
                action_pattern = r"(\w+)\((.*?)\)"
                fun_match = re.search(action_pattern, action_content)
                if fun_match:
                    fun_name = fun_match.group(1)
                    fun_args = fun_match.group(2)
                    args = fun_args.split("&&&")
                    print(f"调用的function: {fun_name}, 参数为：{args}")
                    if fun_name in self.tools:
                        try:
                            obse = self.tools[fun_name](*args)
                        except Exception as e:
                            obse = f"工具执行错误：{str(e)}"
                    else:
                        obse = f"该工具不存在！"
            except Exception as e:
                obse = f"执行出错：{str(e)}"
            print(f"观察到工具执行结果：{obse}")
            messages.append({
                "role":"user",
                "content":f"<observation>{obse}</observation>"
            })

def write_to_file(path, content):
    """将用户的问题和模型的回答写入文件"""
    dir_path = os.path.dirname(path)

    if dir_path and not os.path.exists(dir_path):
        os.makedirs(dir_path)
        print(f"创建目录: {dir_path}")

    with open(path, "a", encoding="utf-8") as file:
        file.write(content+"\n\n")

    return f"文件已成功写入!"

@click.command()
@click.argument("file", type=click.Path(exists=True, file_okay=True, dir_okay=True))
def main(file):
    tools = [write_to_file]
    react = React(tools, file)
    task = input("请输入问题：")
    answer = react.run(task)
    print("\n\nanswer：", answer)

if __name__ == "__main__":
    main()